#include "StringUtils.h"
#include <openssl/rand.h>
#include "sha1sum.h"
#include "Base64.h"

const char * StringUtils::QUOTE_ENCODE = "&quot;";
const char * StringUtils::AMP_ENCODE = "&amp;";
const char * StringUtils::LT_ENCODE = "&lt;";
const char * StringUtils::GT_ENCODE = "&gt;";
const char * StringUtils::numbersAndLetters = "0123456789abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

std::string StringUtils::parseName(std::string & XMPPAddress) {
	int atIndex = XMPPAddress.find_last_of('@');
	if(atIndex == std::string::npos) {
		return "";
	} else {
		return XMPPAddress.substr(0, atIndex + 1);
	}
}

std::string StringUtils::parseServer(std::string & XMPPAddress) {
	int atIndex = XMPPAddress.find_last_of('@');
	if( ++atIndex == XMPPAddress.length()) {
		return "";
	}
	int slashIndex = XMPPAddress.find_last_of("/");
    if (slashIndex > 0 && slashIndex > atIndex) {
        return XMPPAddress.substr(atIndex + 1, slashIndex);
    }
    else {
        return XMPPAddress.substr(atIndex + 1);
    }

}

std::string StringUtils::parseResource(std::string & XMPPAddress) {
	int slashIndex = XMPPAddress.find_first_of("/");
    if (slashIndex + 1 > XMPPAddress.length() || slashIndex < 0) {
        return "";
    }
    else {
        return XMPPAddress.substr(slashIndex + 1);
    }
}

std::string StringUtils::parseBareAddress(std::string & XMPPAddress) {
	int slashIndex = XMPPAddress.find_first_of("/");
    if (slashIndex < 0) {
        return XMPPAddress;
    }
    else if (slashIndex == 0) {
        return "";
    }
    else {
        return XMPPAddress.substr(0, slashIndex);
    }
}

std::string StringUtils::escapeNode(std::string & node) {
	std::string buf;
    for (int i=0, n=node.length(); i<n; i++) {
    	char c = node.at(i);
    	switch (c) {
    		case '"': buf.append("\\22"); break;
            case '&': buf.append("\\26"); break;
            case '\'': buf.append("\\27"); break;
            case '/': buf.append("\\2f"); break;
            case ':': buf.append("\\3a"); break;
            case '<': buf.append("\\3c"); break;
            case '>': buf.append("\\3e"); break;
            case '@': buf.append("\\40"); break;
            case '\\': buf.append("\\5c"); break;
			case ' ': buf.append("\\20"); break;
            default: buf += c;
        }
    }
    return buf;
}

std::string StringUtils::unescapeNode(std::string & node) {
	std::string buf;
    for (int i=0, n=node.length(); i<n; i++) {
        char c = node[i];
        if (c == '\\' && i+2<n) {
            char c2 = node[i+1];
            char c3 = node[i+2];
            if (c2 == '2') {
                switch (c3) {
                    case '0': buf+=' '; i+=2; continue;
                    case '2': buf+='"'; i+=2; continue;
                    case '6': buf+='&'; i+=2; continue;
                    case '7': buf+='\\'; i+=2; continue;
                    case 'f': buf+='/'; i+=2; continue;
                }
            }
            else if (c2 == '3') {
                switch (c3) {
                    case 'a': buf+=':'; i+=2; continue;
                    case 'c': buf+='<'; i+=2; continue;
                    case 'e': buf+='>'; i+=2; continue;
                }
            }
            else if (c2 == '4') {
                if (c3 == '0') {
                    buf += '@';
                    i+=2;
                    continue;
                }
            }
            else if (c2 == '5') {
                if (c3 == 'c') {
                    buf+='\\';
                    i+=2;
                    continue;
                }
            }
        }
        buf += c;
	}
    return buf;
}

std::string StringUtils::escapeForXML(std::string & input) {
	char ch;
    int i=0;
    int last=0;
    int len = input.length();
	std::string out;
    for (; i < len; i++) {
        ch = input[i];
        if (ch > '>') {
        } else if (ch == '<') {
            if (i > last) {
                out.append(input, last, i - last);
            }
            last = i + 1;
            out.append(LT_ENCODE);
        } else if (ch == '>') {
            if (i > last) {
                out.append(input, last, i - last);
            }
            last = i + 1;
            out.append(GT_ENCODE);
        } else if (ch == '&') {
            if (i > last) {
                out.append(input, last, i - last);
            }
            // Do nothing if the string is of the form &#235; (unicode value)
            if (!(len > i + 5 && input[i + 1] == '#' && isdigit(input[i + 2]) 
						&& isdigit(input[i + 3]) && isdigit(input[i + 4]) && input[i + 5] == ';')) {
                last = i + 1;
                out.append(AMP_ENCODE);
            }
        }
        else if (ch == '"') {
            if (i > last) {
                out.append(input, last, i - last);
            }
            last = i + 1;
            out.append(QUOTE_ENCODE);
        }
    }
    if (last == 0) {
        return input;
    }
    if (i > last) {
        out.append(input, last, i - last);
    }
    return out;
}

std::string StringUtils::hash(std::string & data) {
	SHA1_CONTEXT ctx;
	char buf[41];
	sha1_write(&ctx, (unsigned char*)data.c_str(), data.length());
	for( int i = 0; i < 20; ++i )
		sprintf( buf + i * 2,"%02x", ctx.buf[i]);
	return buf;
}

std::string StringUtils::encodeHex(char * bytes) {
	//TODO
	return "";
}

std::string StringUtils::encodeBase64(std::string & data) {
	return base64_encode((const unsigned char*)data.c_str(), data.length());
}

std::string StringUtils::encodeBase64(char * data) {
	return base64_encode((unsigned char*)data, strlen(data));
}

std::string StringUtils::encodeBase64(char * data, bool lineBreaks) {
	//TODO
	return "";
}

std::string StringUtils::encodeBase64(char * data, int offset, int len, bool lineBreaks) {
	//TODO
	return "";
}

std::string StringUtils::decodeBase64(std::string & data) {
	return base64_decode(data);
}

std::string StringUtils::randomString(int length) {
	std::string rand;
	unsigned char out[length];
	int ret,len;
	char filename[50] = {0};

#ifdef _WIN32
	RAND_screen();
#endif

   	while(1) {
    	ret=RAND_status();
		if(ret==1) {
     		printf("seeded enough!\n");
     		break;
		}
    	else {
     		printf("not enough sedded!\n");
     		RAND_poll();
    	}
	}

	const char * p=::RAND_file_name(filename,50);

   	if(p==NULL) {
    	printf("can not get rand file\n");
    	return "";
   	}

   	ret=RAND_write_file(p);
   	len=RAND_load_file(p,50);
   	ret=RAND_bytes(out, length);

	for(int i = 0; i < length; i++) {
		rand += numbersAndLetters[(int)(out[i])%71];
	}

	return rand;
}
